from html.parser import HTMLParser
from html.entities import name2codepoint
from copy import copy

V_START = 1
V_END = 2
V_STANDALONE = 3
V_TEXT = 4

non_containers = ["img", "br", "hr", "meta", "col"]


class PassiveParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.out_buffer = []
        self.tag_chain = []

            
    def emit(self, x):
        self.out_buffer.append(x)
        
    def handle_starttag(self, tag, attrs):
        if tag in non_containers:
            self.emit([V_STANDALONE, tag, attrs])
            return
            #~ print("non-container as a start: " + tag)
            #~ raise Exception
        # fix for stupid html 3.2 tag end omission rules
        if tag in ("p","div") and self.tag_chain[-1]=="p":
            self.tag_chain.pop()
        if tag in ("dt","dd") and self.tag_chain[-1] in ("dt", "dd"):
            self.tag_chain.pop()
        self.tag_chain.append(tag)
        self.emit([V_START, tag, attrs])
        
    def handle_endtag(self, tag):
        #~ if (self.tag_chain[-1] not in ("p","dd","dt")) and (not tag==self.tag_chain[-1]):
            #~ print(self.tag_chain, tag)
            #~ raise
        lasttag = self.tag_chain.pop()
        while lasttag!=tag:
            self.emit([V_END, lasttag])
            lasttag = self.tag_chain.pop()
        self.emit([V_END, tag])
                
    def handle_data(self, data):
        #~ print((data).encode('cp1252','replace'),file=sink)
        self.emit([V_TEXT, data])
    
    def handle_charref(self, name):
        cpoint = int(name)
        if cpoint>255:
            self.handle_data(chr(cpoint))
        else:
            self.handle_data(bytes([cpoint]).decode("cp1252"))

    def handle_entityref(self, name):
        self.handle_data(chr(name2codepoint[name]))

    def handle_startendtag(self, tag, attrs):
        assert tag in non_containers
        self.emit([V_STANDALONE, tag])

def passive_parse(inputfile):
    p = PassiveParser()
    for line in inputfile:
        p.feed(line)
        
        for entry in p.out_buffer:
            yield entry
        p.out_buffer = []
    p.close()
    for entry in p.out_buffer:
        yield entry
